package gob.osinerg.emergencia.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.ClassUtils;

public class GenericDAOImpl extends HibernateDaoSupport implements GenericDAO {


	protected final Logger log;
	
	public GenericDAOImpl() {
		log = Logger.getLogger(this.getClass());
	}

	public Connection getConnection() throws Exception {
		Connection cnx = null;
		cnx = this.getSessionFactory().openStatelessSession().connection();
		return cnx;
	}

	public void delete(Object persistentInstance) throws Exception {
		log.debug("deleting instance");
		try {
			getHibernateTemplate().delete(persistentInstance);
			log.debug("delete successful");
		} catch (Exception e) {
			log.error("delete failed", e);
			throw new Exception(e);
		}
	}

	public void deleteById(Class clazz, Serializable id) throws Exception {
		log.debug("getting instance with id: " + id);
		try {
			Object object = this.findById(clazz, id);
			getHibernateTemplate().delete(object);

		} catch (Exception e) {
			log.error(e);
			throw new Exception(e);
		}
	}

	public boolean exists(Class clazz, String id) throws Exception {
		return false;
	}

	public Object findById(Class clazz, Serializable id) throws Exception {
		log.debug("getting instance with id: " + id);
		try {
			Object instance = getHibernateTemplate().get(clazz, id);
			return instance;
		} catch (Exception e) {
			log.error(e);
			throw new Exception(e);
		}
	}

	public List findByObject(Object object) throws Exception {
		log.debug("getting instance with id: ");
		List list;
		try {
			list = getHibernateTemplate().findByExample(object);
			return list;
		} catch (Exception e) {
			log.error(e);
			throw new Exception(e);
		}
	}

	public Date getCurrentDate() throws Exception {
		String driver = null;
		try {
			driver = getSessionFactory().openStatelessSession().connection()
					.getMetaData().getDriverName();
		} catch (Exception e) {
			throw new Exception(e);
		}
		if (StringUtils.isNotEmpty(driver)) {
			if (driver.indexOf("Oracle") != -1) {
				SQLQuery query = getSessionFactory().getCurrentSession()
						.createSQLQuery("SELECT SYSDATE FROM DUAL");
				return (Date) query.uniqueResult();
			}
			if (driver.indexOf("MySql") != -1) {
				SQLQuery query = getSessionFactory().getCurrentSession()
						.createSQLQuery("SELECT CURDATE()");
				return (Date) query.uniqueResult();
			}
		}
		return null;
	}

	public List getFindCriteria(Object object) throws Exception {
		this.setNull(object);
		List list = null;

		try {
			Criteria c = getCriteria(object);
			list = c.list();

		} catch (Exception e) {
			log.error(e);
			if (ClassUtils.isAssignable(HibernateException.class, e.getClass()))
				throw getHibernateTemplate().convertHibernateAccessException(
						(HibernateException) e);
			else
				throw new Exception(e);
		}
		logger.debug("Find result: " + list.size() + " objects");
		return list;
	}

	private Criteria getCriteria(Object object) throws Exception {
		this.setNull(object);
		Criteria c = getSession().createCriteria(object.getClass());
		try {
			ClassMetadata cm = getSessionFactory().getClassMetadata(
					object.getClass());
			String id = cm.getIdentifierPropertyName();
			String[] fields = getSessionFactory().getClassMetadata(
					object.getClass()).getPropertyNames();

			BeanMap beanMap = new BeanMap(object);
			if (BeanUtils.getProperty(object, id) != null
					&& !"".equals(BeanUtils.getProperty(object, id))) {
				if (cm.getIdentifierType().isComponentType()) {
					BeanMap IdMap = new BeanMap(beanMap.get(id));
					Iterator keys = IdMap.keyIterator();
					while (keys.hasNext()) {
						String key = (String) keys.next();
						if (!"class".equalsIgnoreCase(key)) {
							if (IdMap.get(key) != null) {
								String value = String.valueOf(IdMap.get(key));
								if (value.indexOf("-") == 0) {
									value = value.substring(1);
									c = c.add(Restrictions.ne(id.concat(".")
											.concat(key), IdMap.get(key)));
								} else {
									c = c.add(Restrictions.eq(id.concat(".")
											.concat(key), IdMap.get(key)));
								}
							}
						}
					}
				} else {
					c = c.add(Restrictions.eq(id, beanMap.get(id)));
				}
			}

			for (int i = 0; i < fields.length; i++) {
				if (BeanUtils.getProperty(object, fields[i]) != null) {
					if (String.class.isInstance(beanMap.get(fields[i]))
							&& beanMap.get(fields[i]) != null
							&& !"".equalsIgnoreCase((String) beanMap
									.get(fields[i])))
						c = c.add(Restrictions.ilike(fields[i],
								(String) beanMap.get(fields[i]),
								MatchMode.ANYWHERE));
					else
						c = c.add(Restrictions.eq(fields[i], beanMap
								.get(fields[i])));
				}
			}
		} catch (Exception e) {
			throw new Exception(e);
		}
		return c;
	}

	public List getFindCriteria(Object object, String orderBy)
			throws Exception {
		List list = null;
		try {
			this.setNull(object);
			list = getCriteria(object, orderBy).list();
		} catch (HibernateException e) {
			log.error(e);
			if (ClassUtils.isAssignable(HibernateException.class, e.getClass()))
				throw getHibernateTemplate().convertHibernateAccessException(
						(HibernateException) e);
			else
				throw new Exception(e);
		}
		logger.debug("Find result: " + list.size() + " objects");

		return list;
	}

	protected Criteria getCriteria(Object object, String ordenado)
			throws Exception {
		this.setNull(object);
		Criteria criteria = getCriteria(object);
		if (ordenado.indexOf('.') != -1) {
			String[] ords = ordenado.split("\\.");
			int lgth = ords.length;
			log.debug("ords:" + ordenado + " // length=" + lgth);
			for (int i = 0; i < lgth - 1; i++) {
				if (i == 0)
					criteria = criteria.createAlias(ords[i], "alias" + i);
				else
					criteria = criteria.createAlias("alias" + (i - 1) + "."
							+ ords[i], "alias" + i);
			}
			ordenado = "alias" + (lgth - 2) + "." + ords[lgth - 1];
			log.debug("Ordenado:" + ordenado);
			criteria.addOrder(Order.asc(ordenado));
		} else
			criteria.addOrder(Property.forName(ordenado).asc());
		return criteria;
	}

	public void setNull(Object obj) throws Exception {
		try {

			if (obj != null) {
				if (!ClassUtils.isAssignable(Map.class, obj.getClass())) {
					Field fields[] = obj.getClass().getDeclaredFields();
					for (int i = 0; i < fields.length; ++i) {
						fields[i].setAccessible(true);
						if (fields[i].getType().getName().equals(
								"java.lang.Long")) {
							Long valor = (Long) fields[i].get(obj);
							if (fields[i].get(obj) != null
									&& valor.longValue() == 0) {
								fields[i].set(obj, null);
							}
						}
						if (fields[i].getType().getName().equals(
								"java.lang.String")
								|| fields[i].getType().getName().equals(
										"java.util.Date")) {
							if (fields[i].get(obj) != null
									&& fields[i].get(obj).equals("")) {
								fields[i].set(obj, null);
							}
						}
					}
				} else {
					Map m = (Map) obj;
					Iterator it = m.keySet().iterator();
					while (it.hasNext()) {
						Object key = it.next();
						if (m.get(key) != null
								&& m.get(key).getClass().getName().equals(
										"java.lang.Long")) {
							Long valor = (Long) m.get(key);
							if (m.get(key) != null && valor.longValue() == 0) {
								m.put(key, null);
							}
						}
						if (m.get(key) != null
								&& m.get(key).getClass().getName().equals(
										"java.lang.Double")) {
							Double valor = (Double) m.get(key);
							if (m.get(key) != null && valor.doubleValue() == 0) {
								m.put(key, null);
							}
						}
						if (m.get(key) != null
								&& m.get(key).getClass().getName().equals(
										"java.lang.Integer")) {
							Integer valor = (Integer) m.get(key);
							if (m.get(key) != null && valor.intValue() == 0) {
								m.put(key, null);
							}
						}
						if (m.get(key) != null
								&& (m.get(key).getClass().getName().equals(
										"java.lang.String") || m.get(key)
										.getClass().getName().equals(
												"java.util.Date"))) {
							if (m.get(key) != null && "".equals(m.get(key))) {
								m.put(key, null);
							}
						}
						;
					}
				}
			}
		} catch (Exception e) {
			log.error("error al setear null :" + e.getMessage());
			throw new Exception(e);
		}
	}

	public List getFindCriteria(Object object, String orderBy, String restringe)
			throws Exception {
		return getCriteria(object, orderBy, restringe).list();
	}

	public List getFindCriteria(Object object, int maxResults, String orderBy)
			throws Exception {
		List list = null;
		try {
			Criteria criteria = null;
			this.setNull(object);
			if (StringUtils.isEmpty(orderBy))
				criteria = getCriteria(object);
			else
				criteria = getCriteria(object, orderBy);
			criteria.setMaxResults(maxResults);
			list = criteria.list();
		} catch (Exception e) {
			log.error(e);
			if (ClassUtils.isAssignable(HibernateException.class, e.getClass()))
				throw getHibernateTemplate().convertHibernateAccessException(
						(HibernateException) e);
			else
				throw new Exception(e);
		}
		logger.debug("Find result: " + list.size() + " objects");
		return list;
	}

	protected Criteria getCriteria(Object object, String ordenado,
			String restringeNull) throws Exception {
		Criteria criteria = getCriteria(object, ordenado);
		if (!StringUtils.isEmpty(restringeNull)) {
			String[] rn = restringeNull.split(",");
			for (int i = 0; i < rn.length; i++) {
				criteria.add(Restrictions.isNull(rn[i]));
			}
		}
		return criteria;
	}

	public List getFindCriteria(Object object, int maxResults)
			throws Exception {
		return getFindCriteria(object, maxResults, null);
	}

	public String getIdFieldName(Class clazz) throws Exception {
		return getSessionFactory().getClassMetadata(clazz)
				.getIdentifierPropertyName();
	}

	public void initialize(Object object) throws Exception {
		Hibernate.initialize(object);
	}

	public void initializeCollection(Collection collection)
			throws Exception {
		java.util.Iterator it = collection.iterator();
		while (it.hasNext())
			initialize(it.next());
	}

	public void save(Object object) throws Exception {
		log.debug("saving instance");
		try {
			getHibernateTemplate().saveOrUpdate(object);
			log.debug("save successful");
		} catch (Exception re) {
			log.error("save failed", re);
			throw new Exception(re);
		}
	}

	public void saveCollection(Collection collection) throws Exception {
		java.util.Iterator it = collection.iterator();
		while (it.hasNext())
			save(it.next());
	}

	
	
	public List executeSQLQuery(String namedQuery, Map params ){
		Session session = getSession();

		Query query = session.getNamedQuery(namedQuery);	
		
		if(params != null){
			Set keys = params.keySet();
			for (Iterator i = keys.iterator(); i.hasNext();) {
				 String key = (String) i.next();
				 query.setParameter(key, params.get(key));
			}		
		}
		List list = query.list();
		session.close();

		return list;	
	}	
	
}